{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Teil 3: Fortgeschrittene Remote Execution Werkzeuge\n",
    "\n",
    "Im letzten Abschnitt wurde ein einfaches Model mittels Federated Learning trainiert. Mit `.send()` und `.get()` wurde das Model zu den Trainingsdaten gesendet, ein Update durchgeführt und anschließend zurück gebracht. Doch am Ende des Beispiels wurde klar, dass dies nicht ausreicht um die Privatsphäre der Beteiligten zu schützen. Deshalb wollen wir nun mehrere Gradienten mitteln **bevor** wir sie mit `.get()` zurückholen. Auf diese Weise erhalten wir keinerlei Einsicht in den exakten Gradienten (und schützen so die Privatsphäre des Einzelnen!!!).\n",
    "\n",
    "Um dies zu erreichen, brauchen wir einige weitere Bausteine:\n",
    "\n",
    "- verwenden eines Pointers um einen Tensor zu einem anderen Helfer zu senden\n",
    "\n",
    "Wenn wir gerade dabei sind, werden zusätzlich noch einige weitere fortgeschrittene Tensor Functionen behandelt, welche dieses wie auch spätere Beispiele vereinfachen.\n",
    "\n",
    "Autoren:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "\n",
    "Übersetzer:\n",
    "- Jan Moritz Behnken - Github: [@JMBehnken](https://github.com/JMBehnken)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:33:57.869499Z",
     "start_time": "2020-04-02T14:33:55.869194Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Abschnitt 3.1 - Pointer zu Pointer\n",
    "\n",
    "Wie Sie wissen, können PointerTensor Objekte genau wie normale Tensoren behandelt werden. Um genau zu sein, sie sind _so ähnlich zu Tensoren_, dass wir Pointer **zu** anderen Pointern haben können. Sehen Sie selbst!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:34:14.875339Z",
     "start_time": "2020-04-02T14:34:14.872839Z"
    }
   },
   "outputs": [],
   "source": [
    "bob = sy.VirtualWorker(hook, id='bob')\n",
    "alice = sy.VirtualWorker(hook, id='alice')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:34:16.170803Z",
     "start_time": "2020-04-02T14:34:16.161537Z"
    }
   },
   "outputs": [],
   "source": [
    "# this is a local tensor\n",
    "x = torch.tensor([1,2,3,4])\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:34:17.422518Z",
     "start_time": "2020-04-02T14:34:17.409360Z"
    }
   },
   "outputs": [],
   "source": [
    "# this sends the local tensor to Bob\n",
    "x_ptr = x.send(bob)\n",
    "\n",
    "# this is now a pointer\n",
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:34:21.101861Z",
     "start_time": "2020-04-02T14:34:21.097894Z"
    }
   },
   "outputs": [],
   "source": [
    "# now we can SEND THE POINTER to alice!!!\n",
    "pointer_to_x_ptr = x_ptr.send(alice)\n",
    "\n",
    "pointer_to_x_ptr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Was ist hier genau geschehen?\n",
    "\n",
    "Im vorherigen Beispiel wurde ein Tensor `x` erstellt. Beim Senden des Tensors zu Bob wurde ein Pointer (`x_ptr`) auf dem lokalen Rechner erstellt.\n",
    "\n",
    "Beim Aufrufen von `x_ptr.send(alice)` wurde **dieser Pointer gesendet** an Alice.\n",
    "\n",
    "Anmerkung:  \n",
    "Hierbei wurden die Daten NICHT bewegt! Einzig allein der Pointer auf die Daten wurde verschoben!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:34:55.675117Z",
     "start_time": "2020-04-02T14:34:55.671901Z"
    }
   },
   "outputs": [],
   "source": [
    "# As you can see above, Bob still has the actual data (data is always stored in a LocalTensor type). \n",
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:34:58.673370Z",
     "start_time": "2020-04-02T14:34:58.670353Z"
    }
   },
   "outputs": [],
   "source": [
    "# Alice, on the other hand, has x_ptr!! (notice how it points at bob)\n",
    "alice._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:05.128406Z",
     "start_time": "2020-04-02T14:35:05.124778Z"
    }
   },
   "outputs": [],
   "source": [
    "# and we can use .get() to get x_ptr back from Alice\n",
    "\n",
    "x_ptr = pointer_to_x_ptr.get()\n",
    "x_ptr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:06.438251Z",
     "start_time": "2020-04-02T14:35:06.434487Z"
    }
   },
   "outputs": [],
   "source": [
    "# and then we can use x_ptr to get x back from Bob!\n",
    "\n",
    "x = x_ptr.get()\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Arithmetik mit Pointer -> Pointer -> Daten Objekt\n",
    "\n",
    "Genau wie mit normalen Pointern können beliebige PyTorch Operationen auf den Tensoren angewendet werden. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:28.360456Z",
     "start_time": "2020-04-02T14:35:28.357554Z"
    }
   },
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:29.344013Z",
     "start_time": "2020-04-02T14:35:29.341262Z"
    }
   },
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:31.597999Z",
     "start_time": "2020-04-02T14:35:31.573278Z"
    }
   },
   "outputs": [],
   "source": [
    "p2p2x = torch.tensor([1,2,3,4,5]).send(bob).send(alice)\n",
    "\n",
    "y = p2p2x + p2p2x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:33.654118Z",
     "start_time": "2020-04-02T14:35:33.650251Z"
    }
   },
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:34.537457Z",
     "start_time": "2020-04-02T14:35:34.531543Z"
    }
   },
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:35.671937Z",
     "start_time": "2020-04-02T14:35:35.667221Z"
    }
   },
   "outputs": [],
   "source": [
    "y.get().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:36.336496Z",
     "start_time": "2020-04-02T14:35:36.324648Z"
    }
   },
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:36.966421Z",
     "start_time": "2020-04-02T14:35:36.957757Z"
    }
   },
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:37.732150Z",
     "start_time": "2020-04-02T14:35:37.712875Z"
    }
   },
   "outputs": [],
   "source": [
    "p2p2x.get().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:38.242047Z",
     "start_time": "2020-04-02T14:35:38.231980Z"
    }
   },
   "outputs": [],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:35:56.073964Z",
     "start_time": "2020-04-02T14:35:56.071125Z"
    }
   },
   "outputs": [],
   "source": [
    "alice._objects"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Abschnitt 3.2 - Pointer Ketten Operationen\n",
    "\n",
    "Im letzten Abschnitt wurden die `.send()` und `.get()` Befehle immer direkt auf dem Tensor der lokalen Maschine ausgeführt. Doch bei einer Kette von Pointern kann es manchmal wünschenswert sein Befehle wie `.get()` oder `.send()` auf dem **letzten** Pointer der Kette aufzurufen (wie z. B. das Senden von Daten zwischen Helfern). Um dies zu erreichen, können vertrauliche und speziell dafür entwickelte Funktionen verwendet werden.\n",
    "\n",
    "Diese Operationen sind:\n",
    "\n",
    "- `my_pointer2pointer.move(another_worker)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:36:38.389314Z",
     "start_time": "2020-04-02T14:36:38.386422Z"
    }
   },
   "outputs": [],
   "source": [
    "# x is now a pointer to the data which lives on Bob's machine\n",
    "x = torch.tensor([1,2,3,4,5]).send(bob)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:36:39.561897Z",
     "start_time": "2020-04-02T14:36:39.558392Z"
    }
   },
   "outputs": [],
   "source": [
    "print('  bob:', bob._objects)\n",
    "print('alice:',alice._objects)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:36:41.157713Z",
     "start_time": "2020-04-02T14:36:41.154818Z"
    }
   },
   "outputs": [],
   "source": [
    "x = x.move(alice)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:36:41.939701Z",
     "start_time": "2020-04-02T14:36:41.936490Z"
    }
   },
   "outputs": [],
   "source": [
    "print('  bob:', bob._objects)\n",
    "print('alice:',alice._objects)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-04-02T14:36:43.019255Z",
     "start_time": "2020-04-02T14:36:43.016615Z"
    }
   },
   "outputs": [],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hervorragend! Nun sind Sie bereit um ferngesteuert zusammen mit einem vertrauenswürdigen Aggregator **Gradienten zu mitteln**!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Herzlichen Glückwunsch!!! - Zeit, der Community beizutreten! \n",
    "\n",
    "Herzlichen Glückwunsch zum Abschluss dieses Notebook-Tutorials! Wenn es Ihnen gefallen hat und Sie sich der Bewegung zur Wahrung der Privatsphäre, zum dezentralisiertenen Besitz von KI und der KI-Lieferkette (Daten) anschließen möchten, können Sie dies auf folgende Weise tun! \n",
    "\n",
    "### PySyft auf GitHub einen Stern geben! \n",
    "\n",
    "Der einfachste Weg, unserer Community zu helfen, besteht darin, die GitHub-Repos mit Sternen auszuzeichnen! Dies hilft, das Bewusstsein für die coolen Tools zu schärfen, die wir bauen. \n",
    "\n",
    "- [Gib PySyft einen Stern](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Mach mit bei Slack! \n",
    "\n",
    "Der beste Weg, um über die neuesten Entwicklungen auf dem Laufenden zu bleiben, ist, sich unserer Community anzuschließen! Sie können dies tun, indem Sie das Formular unter [http://slack.openmined.org](http://slack.openmined.org) ausfüllen.\n",
    "\n",
    "### Treten Sie einem Code-Projekt bei! \n",
    "\n",
    "Der beste Weg, um zu unserer Community beizutragen, besteht darin, Entwickler zu werden! Sie können jederzeit zur PySyft GitHub Issues-Seite gehen und nach \"Projects\" filtern. Dies zeigt Ihnen alle Top-Level-Tickets und gibt einen Überblick darüber, an welchen Projekten Sie teilnehmen können! Wenn Sie nicht an einem Projekt teilnehmen möchten, aber ein wenig programmieren möchten, können Sie auch nach weiteren \"einmaligen\" Miniprojekten suchen, indem Sie nach GitHub-Problemen suchen, die als \"good first issue\" gekennzeichnet sind. \n",
    "\n",
    "- [PySyft Projects](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Spenden\n",
    "\n",
    "Wenn Sie keine Zeit haben, zu unserer Codebase beizutragen, aber dennoch Unterstützung leisten möchten, können Sie auch Unterstützer unseres Open Collective werden. Alle Spenden fließen in unser Webhosting und andere Community-Ausgaben wie Hackathons und Meetups! \n",
    "\n",
    " - [OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
